/* BShipServer.java
 * 
 * Distributed object that handles Battleship client requests.
 * Note: Client callback has not been fully integrated.
 * 
 * coded by Darren Woodley */

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.DatagramSocket;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.rmi.server.ExportException;
import java.util.Properties;
import java.util.Vector;
import java.util.*;
import server_output.*;
import data.*;

public class BShipServer implements BShipServerInterface {
	
	private PlayerTable pt;
	private MulticastSocket chatSocket;
	private DatagramSocket testSocket;
	private InetAddress group;
	private Vector<ClientInterface> clientList;
	private int map[][][] = new int[70][50][7];
	
	
	private final int CHAT_PORT = 21212;
	private final static int RMI_PORT = 1099;
	
	public BShipServer() throws RemoteException
	{
		super();
		pt = PlayerTable.getInstance();
		clientList = new Vector<ClientInterface>();
		try
		{
			chatSocket = new MulticastSocket(CHAT_PORT);
			testSocket = new DatagramSocket();
			chatSocket.setTimeToLive(1);
			group = InetAddress.getByName("224.0.0.1");
			chatSocket.joinGroup(group);
		}
		catch(IOException ioe)
		{
			ServerOutput.getFrame().addToConn("Exception in BShipServerImpl:");
			System.out.println("Exception in BShipServerImpl:");
			ioe.printStackTrace();
		}
	}


	@Override
	public boolean login(String username, String hostIP) throws RemoteException 
	{
		try
    	{
			ServerOutput.getFrame().addToConn("Login request from " + hostIP);
    		System.out.println("Login request from " + hostIP);
    		InetAddress address = InetAddress.getByName(hostIP);
    		boolean success = PlayerTable.getInstance().addPlayer(username, address);
			if(success)
			{
				String str = username + " has logged in";
				ServerOutput.getFrame().addToConn(str);
				System.out.println(str);
				DatagramPacket dp = new DatagramPacket(str.getBytes(), str.length(), group, CHAT_PORT);
				chatSocket.send(dp);
				
			}
    		return success;
			
    	}
    	catch(Exception e)
    	{
			ServerOutput.getFrame().addToConn("Exception in BShipServerImpl:");
    		System.out.println("Exception in BShipServerImpl:");
    		e.printStackTrace();
    		return false;
    	}
	}

	@Override
	public boolean logout(String username) throws RemoteException {
		boolean success = PlayerTable.getInstance().removePlayer(username);
		if(success)
		{
			try
			{
				String str = username + " has logged out";
				ServerOutput.getFrame().addToConn(str);
				System.out.println(str);
				DatagramPacket dp = new DatagramPacket(str.getBytes(), str.length(), group, CHAT_PORT);
				chatSocket.send(dp);
			}
			catch(IOException ioe)
			{
				System.out.println("IOException found in BShipServer.logout:");
				ioe.printStackTrace();
			}
		}
		return success;
	}
	
	@Override
	public boolean sendMessage(String user, String mess) throws RemoteException {
		String str = user + ": " + mess;
		
		ServerOutput.getFrame().addToChat(str);
		System.out.println(str);
	
        try
        {
            DatagramPacket dp = new DatagramPacket(str.getBytes(), str.getBytes().length, group, CHAT_PORT);
            chatSocket.send(dp);
            return true; 
        }
        catch(IOException ioe)
        {
			ServerOutput.getFrame().addToChat("Exception in BShipImpl.sendMessage:");
        	System.out.println("Exception in BShipImpl.sendMessage:");
        	ioe.printStackTrace();
         return false;
        }

	}
	
	public boolean startGame() throws RemoteException
	{
		return true;
	}

	@Override
	public boolean placeShip(String player, char shipType, char align, int x, int y) throws RemoteException
	{
		//Process shipType and change Matrix
		if (shipType == 'C')
		{
			if (align == 'V')
			{
				map[x][y][1] = 2;
				map[x][y+1][1] = 7;
				map[x][y+2][1] = 7;
				map[x][y+3][1] = 7;
				map[x][y+4][1] = 3;
			}

			if (align == 'H')
			{
				map[x][y][1] = 5;
				map[x+1][y][1] = 6;
				map[x+2][y][1] = 6;
				map[x+3][y][1] = 6;
				map[x+4][y][1] = 4;
			}
		}

		if (shipType == 'B')
		{
			if (align == 'V')
			{
				map[x][y][1] = 2;
				map[x][y+1][1] = 7;
				map[x][y+2][1] = 7;
				map[x][y+3][1] = 3;
			}

			if (align == 'H')
			{				
				map[x][y][1] = 5;
				map[x+1][y][1] = 6;
				map[x+2][y][1] = 6;
				map[x+3][y][1] = 4;
			}
		}

		if (shipType == 'D')
		{
			if (align == 'V')
			{				
				map[x][y][1] = 2;
				map[x][y+1][1] = 7;
				map[x][y+2][1] = 3;
			}

			if (align == 'H')
			{
				map[x][y][1] = 5;
				map[x+1][y][1] = 6;
				map[x+2][y][1] = 4;
			}
		}

		if (shipType == 'S')
		{
			if (align == 'V')
			{
				map[x][y][1] = 2;
				map[x][y+1][1] = 7;
				map[x][y+2][1] = 3;
			}

			if (align == 'H')
			{
				map[x][y][1] = 5;
				map[x+1][y][1] = 6;
				map[x+2][y][1] = 4;
			}
		}

		if (shipType == 'P')
		{
			if (align == 'V')
			{
				map[x][y][1] = 2;
				map[x][y+1][1] = 3;
			}

			if (align == 'H')
			{
				map[x][y][1] = 5;
				map[x+1][y][1] = 4;
			}
		}
		String str = ("Player " + player + " placed ship at " + x + ", " + y);
		ServerOutput.getFrame().addToGame(str);
		return true;
	}

	@Override
	public boolean fire(String player, int x, int y, int result) throws RemoteException {
		int playerIndex = -1;
		for(int i = 0; i < pt.getPlayers().size(); i++)
		{
			if(pt.getPlayers().get(i).getName() == player)
			{
				playerIndex = i;
				break;
			}
		}

		map[x][y][playerIndex + 1] = result;
		String str = ("Player " + player + " was fired on @ " + x + ", " + y);
		ServerOutput.getFrame().addToGame(str);
	
		return true;
	}
	
	public boolean registerForCallback(ClientInterface client) throws RemoteException
	{
		if(!clientList.contains(client))
		{
			clientList.addElement(client);
			ServerOutput.getFrame().addToConn("Registered new client");
			System.out.println("Registered new client");
			return true;
		}
		else
		{
			ServerOutput.getFrame().addToConn("Client is already registered");
			System.out.println("Client is already registered");
			return false;
		}
	}

	public boolean unregisterForCallback(ClientInterface client) throws RemoteException
	{
		if(clientList.contains(client))
		{
			clientList.removeElement(client);
			ServerOutput.getFrame().addToConn("Client unregistered");
			System.out.println("Client unregistered");
			return true;
		}
		else
		{
			ServerOutput.getFrame().addToConn("Client is not registered!");
			System.out.println("Client is not registered!");
			return false;
		}
	}

	private static Registry startRegistry(int RMIportNum) throws RemoteException
	{
		Registry registry;
		try
		{
			registry = LocateRegistry.getRegistry(RMIportNum);
			registry.list();
		}
		catch(RemoteException re)
		{
			ServerOutput.getFrame().addToConn("RMI registry cannot be located at port " + RMIportNum);
			System.out.println("RMI registry cannot be located at port " + RMIportNum);
			registry = LocateRegistry.createRegistry(RMIportNum);
			ServerOutput.getFrame().addToConn("RMI registry created at port " + RMIportNum);
			System.out.println("RMI registry created at port " + RMIportNum);
		}
		
		return registry;
	}
	
	
	public Vector<String> getPlayers()
	{
		Vector<String> temp = new Vector<String>();
		for(Player p : pt.getPlayers())
			temp.add(p.getName());
		return temp;
	}
	
	public static void main(String[] args)
	{
		System.setProperty("java.security.policy", "server.policy");
		// create security to allow port access;
		if(System.getSecurityManager() == null)
			System.setSecurityManager(new SecurityManager());
		
		try
		{
			String name = "bship";
			BShipServerInterface bship = new BShipServer();
			System.out.println("Exporting object");
			BShipServerInterface stub = (BShipServerInterface)UnicastRemoteObject.exportObject(bship, 0);
			System.out.println("Creating registry");
			Registry reg = LocateRegistry.getRegistry();
			if(reg == null)
				reg = LocateRegistry.createRegistry(RMI_PORT);
			System.out.println("Registry created");
			String[] list = reg.list();
			for(String str : list)
				System.out.println(str);
			reg.rebind(name, stub);
			System.out.println("BShipServer bound");
			ServerOutput so = ServerOutput.getFrame();
		}
		catch(ExportException ee)
		{
			System.out.println("ExportException in BShipServer.main");
			ee.printStackTrace();
		}
		catch(Exception e)
		{
			System.out.println("Exception found in BShipServer.main:");
			e.printStackTrace();
		}
	}
}
